home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1865 / 1865.xpi / chrome / adblockplus.jar / content / requests.js < prev    next >
Text File  |  2010-01-07  |  12KB  |  482 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Adblock Plus.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Wladimir Palant.
  18.  * Portions created by the Initial Developer are Copyright (C) 2006-2009
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *
  23.  * ***** END LICENSE BLOCK ***** */
  24.  
  25. /**
  26.  * @fileOverview Stores Adblock Plus data to be attached to a window.
  27.  * This file is included from AdblockPlus.js.
  28.  */
  29.  
  30. const dataSeed = Math.random();    // Make sure our properties have randomized names
  31. const docDataProp = "abpDocData" + dataSeed;
  32. const nodeDataProp = "abpNodeData" + dataSeed;
  33. const nodeIndexProp = "abpNodeIndex" + dataSeed;
  34. var nodeIndex = 0;
  35.  
  36. function RequestList(wnd) {
  37.     this.entries = {__proto__: null};
  38.     this.urls = {__proto__: null};
  39.     this.install(wnd);
  40. }
  41. abp.RequestList = RequestList;
  42. abp.DataContainer = RequestList;    // For sake of ABP Watcher
  43.  
  44. RequestList.prototype = {
  45.     entries: null,
  46.     urls: null,
  47.     topList: null,
  48.     lastSelection: null,
  49.     detached: false,
  50.  
  51.     /**
  52.      * Weak reference to the window this data is attached to.
  53.      * @type nsIWeakReference
  54.      */
  55.     window: null,
  56.     /**
  57.      * Counter to be incremented every time an entry is added - list will be compacted when a threshold is reached.
  58.      * @type Integer
  59.      */
  60.     _compactCounter: 0,
  61.     /**
  62.      * Time in milliseconds of the last list cleanup, makes sure cleanup isn't triggered too often.
  63.      * @type Integer
  64.      */
  65.     _lastCompact: 0,
  66.  
  67.     /**
  68.      * Attaches this request list to a window.
  69.      */
  70.     install: function(/**Window*/ wnd)
  71.     {
  72.         this.window = getWeakReference(wnd);
  73.         wnd.document[docDataProp] = this;
  74.  
  75.         let topWnd = wnd.top;
  76.         if (topWnd != wnd)
  77.         {
  78.             this.topList = RequestList.getDataForWindow(topWnd);
  79.             this.topList.notifyListeners("refresh");
  80.         }
  81.         else
  82.             this.topList = this;
  83.  
  84.         let me = this;
  85.         wnd.addEventListener("pagehide", function(ev)
  86.         {
  87.             if (!ev.isTrusted || ev.eventPhase != ev.AT_TARGET)
  88.                 return;
  89.  
  90.             if (me == me.topList)
  91.                 me.notifyListeners("clear");
  92.  
  93.             // We shouldn't send further notifications
  94.             me.detached = true;
  95.  
  96.             if (me != me.topList)
  97.                 me.topList.notifyListeners("refresh");
  98.         }, false);
  99.         wnd.addEventListener("pageshow", function(ev)
  100.         {
  101.             if (!ev.isTrusted || ev.eventPhase != ev.AT_TARGET)
  102.                 return;
  103.  
  104.             // Allow notifications again
  105.             me.detached = false;
  106.  
  107.             if (me != me.topList)
  108.                 me.topList.notifyListeners("refresh");
  109.             else
  110.                 me.notifyListeners("select");
  111.         }, false);
  112.     },
  113.  
  114.     /**
  115.      * Notifies all listeners about changes in this list or one of its sublists.
  116.      * @param {String} type   type of notification, one of "add", "refresh", "select", "clear"
  117.      * @param {RequestEntry} entry   data entry being updated (only present for type "add")
  118.      */
  119.     notifyListeners: function(type, entry)
  120.     {
  121.         let wnd = getReferencee(this.window);
  122.         if (this.detached || !wnd)
  123.             return;
  124.  
  125.         for each (let listener in RequestList._listeners)
  126.             listener(wnd, type, this, entry);
  127.     },
  128.  
  129.     addNode: function(node, contentType, docDomain, thirdParty, location, filter, objTab)
  130.     {
  131.         // for images repeated on page store node for each repeated image
  132.         let key = " " + contentType + " " + location;
  133.         let entry;
  134.         let isNew = !(key in this.entries);
  135.         if (isNew)
  136.             this.entries[key] = this.urls[location] = entry = new RequestEntry(key, contentType, docDomain, thirdParty, location);
  137.         else
  138.             entry = this.entries[key];
  139.  
  140.         // Always override the filter just in case a known node has been blocked
  141.         if (filter)
  142.             entry.filter = filter;
  143.  
  144.         entry.addNode(node);
  145.         if (objTab)
  146.             entry.addNode(objTab);
  147.  
  148.         if (isNew)
  149.             this.topList.notifyListeners("add", this.entries[key]);
  150.  
  151.         // Compact the list of entries after 100 additions but at most once every 5 seconds
  152.         if (isNew && ++this._compactCounter >= 100 && Date.now() - this._lastCompact > 5000)
  153.             this.getAllLocations();
  154.  
  155.         return entry;
  156.     },
  157.  
  158.     getLocation: function(type, location)
  159.     {
  160.         let key = " " + type + " " + location;
  161.         if (key in this.entries)
  162.             return this.entries[key];
  163.  
  164.         let wnd = getReferencee(this.window);
  165.         let numFrames = (wnd ? wnd.frames.length : -1);
  166.         for (let i = 0; i < numFrames; i++)
  167.         {
  168.             let frameData = RequestList.getDataForWindow(wnd.frames[i], true);
  169.             if (frameData && !frameData.detached)
  170.             {
  171.                 let result = frameData.getLocation(type, location);
  172.                 if (result)
  173.                     return result;
  174.             }
  175.         }
  176.  
  177.         return null;
  178.     },
  179.  
  180.     getAllLocations: function(results, hadOutdated)
  181.     {
  182.         let now = Date.now();
  183.  
  184.         // Accessing wnd.frames will flush outstanding content policy requests in Gecko 1.9.0/1.9.1.
  185.         // Access it now to make sure we return the correct result even if more nodes are added here.
  186.         let wnd = getReferencee(this.window);
  187.         let frames = wnd.frames;
  188.  
  189.         this._compactCounter = 0;
  190.         this._lastCompact = now;
  191.  
  192.         if (typeof results == "undefined")
  193.             results = [];
  194.  
  195.         let recursiveCall = true;
  196.         if (typeof hadOutdated == "undefined")
  197.         {
  198.             recursiveCall = false;
  199.             hadOutdated = {value: false};
  200.         }
  201.         for (var key in this.entries)
  202.         {
  203.             if (key[0] == " ")
  204.             {
  205.                 let entry = this.entries[key];
  206.                 if (!entry.hasAdditionalNodes && now - entry.lastUpdate >= 60000 && !entry.nodes.length)
  207.                 {
  208.                     hadOutdated.value = true;
  209.                     delete this.entries[key];
  210.                 }
  211.                 else
  212.                     results.push(this.entries[key]);
  213.             }
  214.         }
  215.  
  216.         let numFrames = (wnd ? frames.length : -1);
  217.         for (let i = 0; i < numFrames; i++)
  218.         {
  219.             let frameData = RequestList.getDataForWindow(frames[i], true);
  220.             if (frameData && !frameData.detached)
  221.                 frameData.getAllLocations(results, hadOutdated);
  222.         }
  223.  
  224.         if (!recursiveCall && hadOutdated.value)
  225.             this.topList.notifyListeners("refresh");
  226.  
  227.         return results;
  228.     },
  229.  
  230.     getURLInfo: function(location)
  231.     {
  232.         return (location in this.urls ? this.urls[location] : null);
  233.     }
  234. };
  235.  
  236. /**
  237.  * Retrieves the data list associated with a window.
  238.  * @param {Window} window
  239.  * @param {Boolean} noInstall  if missing or false, a new empty list will be created and returned if no data is associated with the window yet.
  240.  * @result {RequestList}
  241.  * @static
  242.  */
  243. RequestList.getDataForWindow = function(wnd, noInstall)
  244. {
  245.     if (wnd.document && docDataProp in wnd.document)
  246.         return wnd.document[docDataProp];
  247.     else if (!noInstall)
  248.         return new RequestList(wnd);
  249.     else
  250.         return null;
  251. };
  252.  
  253. /**
  254.  * Retrieves the data entry associated with the document element.
  255.  * @param {Node} node
  256.  * @param {Boolean} noParent  if missing or false, the search will extend to the parent nodes until one is found that has data associated with it
  257.  * @result {RequestEntry}
  258.  * @static
  259.  */
  260. RequestList.getDataForNode = function(node, noParent)
  261. {
  262.     while (node)
  263.     {
  264.         let entryKey = node.getUserData(nodeDataProp);
  265.         if (entryKey)
  266.         {
  267.             let wnd = getWindow(node);
  268.             let data = (wnd ? RequestList.getDataForWindow(wnd, true) : null);
  269.             if (data && entryKey in data.entries)
  270.                 return [node, data.entries[entryKey]];
  271.         }
  272.  
  273.         if (typeof noParent == "boolean" && noParent)
  274.             return null;
  275.  
  276.         // If we don't have any information on the node, then maybe on its parent
  277.         node = node.parentNode;
  278.     }
  279.  
  280.     return null;
  281. };
  282.  
  283. /**
  284.  * List of registered data listeners
  285.  * @type Array of Function
  286.  * @static
  287.  */
  288. RequestList._listeners = [];
  289.  
  290. /**
  291.  * Adds a new listener to be notified whenever new requests are added to the list.
  292.  * @static
  293.  */
  294. RequestList.addListener = function(/**Function*/ listener)
  295. {
  296.     RequestList._listeners.push(listener);
  297. };
  298.     
  299. /**
  300.  * Removes a listener.
  301.  * @static
  302.  */
  303. RequestList.removeListener = function(/**Function*/ listener)
  304. {
  305.     for (var i = 0; i < RequestList._listeners.length; i++)
  306.         if (RequestList._listeners[i] == listener)
  307.             RequestList._listeners.splice(i--, 1);
  308. };
  309.  
  310. function RequestEntry(key, contentType, docDomain, thirdParty, location)
  311. {
  312.     this._nodes = [];
  313.     this._indexes = [];
  314.     this.key = key;
  315.     this.type = contentType;
  316.     this.docDomain = docDomain;
  317.     this.thirdParty = thirdParty;
  318.     this.location = location;
  319. }
  320. RequestEntry.prototype =
  321. {
  322.     /**
  323.      * Document elements associated with this entry (stored as weak references)
  324.      * @type Array of nsIWeakReference
  325.      */
  326.     _nodes: null,
  327.     /**
  328.      * Nodes indexes corresponding with the nodes - used to recognize outdated entries.
  329.      * @type Array of Integer
  330.      */
  331.     _indexes: null,
  332.     /**
  333.      * Will be set to true if the entry is associated with other nodes besides the
  334.      * ones listed in the nodes property - used if obtaining a weak reference to
  335.      * some nodes isn't possible.
  336.      * @type Boolean
  337.      */
  338.     hasAdditionalNodes: false,
  339.     /**
  340.      * Counter to be incremented every time a node is added - list will be compacted when a threshold is reached.
  341.      * @type Integer
  342.      */
  343.     _compactCounter: 0,
  344.     /**
  345.      * Time in milliseconds of the last list cleanup, makes sure cleanup isn't triggered too often.
  346.      * @type Integer
  347.      */
  348.     _lastCompact: 0,
  349.     /**
  350.      * Time out last node addition or compact operation (used to find outdated entries).
  351.      * @type Integer
  352.      */
  353.     lastUpdate: 0,
  354.     /**
  355.      * ID of this entry in document's list
  356.      * @type String
  357.      */
  358.     key: null,
  359.     /**
  360.      * Content type of the request (one of the nsIContentPolicy constants)
  361.      * @type Integer
  362.      */
  363.     type: null,
  364.     /**
  365.      * Domain name of the requesting document
  366.      * @type String
  367.      */
  368.     docDomain: null,
  369.     /**
  370.      * True if the request goes to a different domain than the domain of the containing document
  371.      * @type Boolean
  372.      */
  373.     thirdParty: false,
  374.     /**
  375.      * Address being requested
  376.      * @type String
  377.      */
  378.     location: null,
  379.     /**
  380.      * Filter that was applied to this request (if any)
  381.      * @type Filter
  382.      */
  383.     filter: null,
  384.     /**
  385.      * Document elements associated with this entry
  386.      * @type Array of Element
  387.      */
  388.     get nodes()
  389.     {
  390.         this._compactCounter = 0;
  391.         this.lastUpdate = this._lastCompact = Date.now();
  392.  
  393.         let result = [];
  394.         for (let i = 0; i < this._nodes.length; i++)
  395.         {
  396.             let node = getReferencee(this._nodes[i]);
  397.  
  398.             // Remove node if associated with a different weak reference - this node was added to a different list already
  399.             if (node && node.getUserData(nodeIndexProp) == this._indexes[i])
  400.                 result.push(node);
  401.             else
  402.             {
  403.                 this._nodes.splice(i, 1);
  404.                 this._indexes.splice(i, 1);
  405.                 i--;
  406.             }
  407.         }
  408.         return result;
  409.     },
  410.     /**
  411.      * String representation of the content type, e.g. "subdocument"
  412.      * @type String
  413.      */
  414.     get typeDescr() policy.typeDescr[this.type],
  415.     /**
  416.      * User-visible localized representation of the content type, e.g. "frame"
  417.      * @type String
  418.      */
  419.     get localizedDescr() policy.localizedDescr[this.type],
  420.  
  421.     /**
  422.      * Adds a new document element to be associated with this request.
  423.      */
  424.     addNode: function(/**Node*/ node)
  425.     {
  426.         // Compact the list of nodes after 100 additions but at most once every 5 seconds
  427.         if (++this._compactCounter >= 100 && Date.now() - this._lastCompact > 5000)
  428.             this.nodes;
  429.         else
  430.             this.lastUpdate = Date.now();
  431.  
  432.         node.setUserData(nodeDataProp, this.key, null);
  433.  
  434.         let weakRef = getWeakReference(node);
  435.         if (weakRef)
  436.         {
  437.             this._nodes.push(weakRef);
  438.  
  439.             ++nodeIndex;
  440.             node.setUserData(nodeIndexProp, nodeIndex, null);
  441.             this._indexes.push(nodeIndex);
  442.         }
  443.         else
  444.             this.hasAdditionalNodes = true;
  445.     },
  446.  
  447.     /**
  448.      * Resets the list of document elements associated with this entry.
  449.      * @return {Array of Node} old list of elements
  450.      */
  451.     clearNodes: function()
  452.     {
  453.         let result = this.nodes;
  454.         this._nodes = [];
  455.         this._indexes = [];
  456.         return result;
  457.     }
  458. };
  459.  
  460. /**
  461.  * Stores a weak reference to a DOM node (will store a reference to original node if wrapped).
  462.  */
  463. function getWeakReference(/**nsISupports*/ node) /**nsIWeakReference*/
  464. {
  465.     if (node instanceof Ci.nsISupportsWeakReference)
  466.         return node.GetWeakReference();
  467.     else
  468.         return null;
  469. }
  470.  
  471. /**
  472.  * Retrieves a DOM node from a weak reference, restores XPCNativeWrapper if necessary.
  473.  */
  474. function getReferencee(/**nsIWeakReference*/ weakRef) /**nsISupports*/
  475. {
  476.     try {
  477.         return weakRef.QueryReferent(Ci.nsISupports);
  478.     } catch (e) {
  479.         return null;
  480.     }
  481. }
  482.